/*
 * Albareth - an action roleplaying game.
 * Copyright (C) 2008 Jens Anuth
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
 *   02110-1301, USA.
 * 
 * $Id: InGameState.java 47 2008-05-16 21:01:33Z jens464 $
*/
package albareth.frontend.slick.state;

import java.util.Iterator;
import java.util.List;

import org.newdawn.slick.Color;
import org.newdawn.slick.Graphics;
import org.newdawn.slick.Input;
import org.newdawn.slick.SlickException;

import albareth.core.Game;
import albareth.core.GameEffect;
import albareth.core.action.Action;
import albareth.core.action.Spell;
import albareth.core.entity.Activatable;
import albareth.core.entity.Entity;
import albareth.core.entity.Item;
import albareth.core.entity.ItemContainer;
import albareth.core.entity.ItemContainerChangeListener;
import albareth.core.entity.Mobile;
import albareth.core.entity.PlayerCharacter;
import albareth.frontend.Resources;
import albareth.frontend.slick.SlickFrontend;
import albareth.frontend.slick.SlickResources;
import albareth.frontend.slick.ui.ActionBar;
import albareth.frontend.slick.ui.BasicMenuFrame;
import albareth.frontend.slick.ui.BuffFrame;
import albareth.frontend.slick.ui.CastingBar;
import albareth.frontend.slick.ui.ContainerFrame;
import albareth.frontend.slick.ui.DungeonWindow;
import albareth.frontend.slick.ui.EquipmentFrame;
import albareth.frontend.slick.ui.GlidingScroll;
import albareth.frontend.slick.ui.InventoryFrame;
import albareth.frontend.slick.ui.MapOverviewFrame;
import albareth.frontend.slick.ui.MessageWindow;
import albareth.frontend.slick.ui.MiniStatusFrame;
import albareth.frontend.slick.ui.PlayerCharStatusFrame;
import albareth.frontend.slick.ui.ScrollDialog;
import albareth.frontend.slick.ui.TargetFrame;
import albareth.frontend.slick.ui.TooltipFrame;
import albareth.frontend.slick.ui.UIButton;
import albareth.frontend.slick.ui.UIButtonListener;
import albareth.frontend.slick.ui.UIElement;
import albareth.frontend.slick.ui.UIToggleButton;
import albareth.tests.SchedulerTestFrame;
import albareth.util.Location;
import frosch.util.Direction;
import frosch.util.FToolkit;
import frosch.util.glider.Easing;

public class InGameState extends AbstractAlbarethGameState
    implements ItemContainerChangeListener, UIButtonListener
{
    public static final String COMP_STATUS                = "c_player_Status";
    public static final String COMP_OVERVIEW              = "c_overview";
    public static final String COMP_INVENTORY             = "c_inventory";
    public static final String COMP_MESSAGES              = "c_messages";
    public static final String COMP_CONTEXTMENU_INVENTORY = "c_contextmenu";
    public static final String COMP_EQUIPMENT             = "c_equipment";
    public static final String COMP_CONTAINER             = "c_container";
    
    public static final int MULTI_FRAME_UPPER   = 0; 
    public static final int MULTI_FRAME_CENTRAL = 1;
    public static final int MULTI_FRAME_LOWER   = 2;
    
    private static final String BUTTON_EQ = "eq_btn";
    private static final String BUTTON_INV = "inv_btn";
    private static final String BUTTON_MAP = "map_btn";
    
    private static final int CMD_MOVE_NORTH     = Input.KEY_W;
    private static final int CMD_MOVE_NORTH_2   = Input.KEY_UP;
    private static final int CMD_MOVE_SOUTH     = Input.KEY_S;
    private static final int CMD_MOVE_SOUTH_2   = Input.KEY_DOWN;
    private static final int CMD_MOVE_SOUTH_3   = Input.KEY_X;
    private static final int CMD_MOVE_EAST      = Input.KEY_D;
    private static final int CMD_MOVE_EAST_2    = Input.KEY_RIGHT;
    private static final int CMD_MOVE_WEST      = Input.KEY_A;
    private static final int CMD_MOVE_WEST_2    = Input.KEY_LEFT;
    private static final int CMD_MOVE_NORTHWEST = Input.KEY_Q;
    private static final int CMD_MOVE_SOUTHWEST = Input.KEY_Y;
    private static final int CMD_MOVE_NORTHEAST = Input.KEY_E;
    private static final int CMD_MOVE_SOUTHEAST = Input.KEY_C;
    
    //private static final int CMD_ACTION_1       = Input.KEY_1;
    private static final int CMD_ABORT          = Input.KEY_ESCAPE;
    private static final int CMD_MAP            = Input.KEY_M;
    private static final int CMD_INVENTORY      = Input.KEY_I;
    private static final int CMD_INVENTORY_2    = Input.KEY_B;
    private static final int CMD_CHAR_INFO      = Input.KEY_C;
    private static final int CMD_CHAR_INFO_2      = Input.KEY_E;
    private static final int CMD_USE            = Input.KEY_RETURN;

    //dimensions of upper, central and lower right multi frames
    private static final int[] MULTI_FRAME_X = new int[3];
    private static final int[] MULTI_FRAME_Y = new int[3];
    private static final int[] MULTI_FRAME_WIDTH = new int[3];
    private static final int[] MULTI_FRAME_HEIGHT = new int[3];
    static
    {
        MULTI_FRAME_X[MULTI_FRAME_UPPER] = 460;
        MULTI_FRAME_Y[MULTI_FRAME_UPPER] = 25;
        MULTI_FRAME_WIDTH[MULTI_FRAME_UPPER] = 310;
        MULTI_FRAME_HEIGHT[MULTI_FRAME_UPPER] = 190;

        MULTI_FRAME_X[MULTI_FRAME_CENTRAL] = 460;
        MULTI_FRAME_Y[MULTI_FRAME_CENTRAL] = 232;
        MULTI_FRAME_WIDTH[MULTI_FRAME_CENTRAL] = 310;
        MULTI_FRAME_HEIGHT[MULTI_FRAME_CENTRAL] = 50;

        MULTI_FRAME_X[MULTI_FRAME_LOWER] = 460;
        MULTI_FRAME_Y[MULTI_FRAME_LOWER] = 298;
        MULTI_FRAME_WIDTH[MULTI_FRAME_LOWER] = 310;
        MULTI_FRAME_HEIGHT[MULTI_FRAME_LOWER] = 279;
    }
    
    private UIElement[] _multiFrame        = new UIElement[3];
    private UIElement[] _multiFrameDefault = new UIElement[3];

    private ActionBar _actionBar;
    private CastingBar _castingBar;
    private InventoryFrame _inventoryFrame;
    private MessageWindow _messageWindow;
    private DungeonWindow _dungeonWindow;
    private TargetFrame _targetFrame;
    private PlayerCharStatusFrame _statusFrame;
    private MiniStatusFrame _miniStatus;
    private TooltipFrame _tooltipFrame;
    private BuffFrame _buffFrame;
    private ContainerFrame _containerFrame = null;
    private UIToggleButton _eqBtn;
    private UIToggleButton _invBtn;
    private UIToggleButton _mapBtn;
    
    private SchedulerTestFrame _schedulerFrame;

    //private Game _game = null;

    
    public InGameState(SlickFrontend frontend)
    {
        super(frontend);
    }
    
    public int getID()
    {
        return SlickFrontend.STATE_INGAME;
    }
    
    /*
    public void setGame(Game game)
    {
        _game = game;
    }
    */
    
    public void init() throws SlickException
    {
        setBackgroundImage(SlickResources.IN_GAME_BG);
    }


    public void enter() throws SlickException
    {
        removeAllUIElements();
        
        PlayerCharacter self = _frontend.getPlayerCharacter();
        

        _messageWindow = new MessageWindow();
        //add action bar:
        _actionBar = new ActionBar(
                _frontend.getPlayerCharacter(), 32, 32, 12, 2
        );
        _actionBar.setLocation(30, 497);
        _actionBar.setPadding(8, 6);
        _actionBar.setBackgroundImage(SlickResources.QUICKBAR);
        _actionBar.setSize(
                SlickResources.QUICKBAR.getWidth(),
                SlickResources.QUICKBAR.getHeight()
        );
        
        //TODO: dynamically add all spells and activatable items of the player
        addUIElement(_actionBar);
        fillActionBar();

        _tooltipFrame = new TooltipFrame(30, 427);
        _tooltipFrame.setSize(400, 20);
        addUIElement(_tooltipFrame);
        
        //add casting bar:
        _castingBar = new CastingBar(130, 470, self);
        addUIElement(_castingBar);

        _dungeonWindow = new DungeonWindow(38, 32, 384, 384, _frontend);
        addUIElement(_dungeonWindow);
        
        //SwingCooldownFrame cdFrame = new SwingCooldownFrame(28, 470, 100, 40, _frontend);
        //addUIElement(cdFrame);

        _buffFrame = new BuffFrame(self, 1, 4);
        _buffFrame.setLocation(740, 24);
        addUIElement(_buffFrame);

        //_mapBtn = new UIToggleButton(428, 30, 26, 28);
        _mapBtn = new UIToggleButton(343, 467, 26, 28);
        _mapBtn.setTooltipText("Map (M)");
        _mapBtn.setMouseDownImage(SlickResources.BUTTON_MAP_PRESSED);
        _mapBtn.setMouseUpImage(SlickResources.BUTTON_MAP_NORMAL);
        _mapBtn.setMouseOverImage(SlickResources.BUTTON_MAP_NORMAL);
        _mapBtn.setId(BUTTON_MAP);
        _mapBtn.addButtonListener(this);
        addUIElement(_mapBtn);

        //_eqBtn = new UIToggleButton(428, 61, 26, 28);
        _eqBtn = new UIToggleButton(373, 467, 26, 28);
        _eqBtn.setTooltipText("Equipment (E)");
        _eqBtn.setMouseDownImage(SlickResources.BUTTON_EQ_PRESSED);
        _eqBtn.setMouseUpImage(SlickResources.BUTTON_EQ_NORMAL);
        _eqBtn.setMouseOverImage(SlickResources.BUTTON_EQ_NORMAL);
        _eqBtn.setId(BUTTON_EQ);
        _eqBtn.addButtonListener(this);
        addUIElement(_eqBtn);

        //_invBtn = new UIToggleButton(428, 93, 26, 28);
        _invBtn = new UIToggleButton(403, 467, 26, 28);
        _invBtn.setTooltipText("Inventory (I)");
        _invBtn.setMouseDownImage(SlickResources.BUTTON_INV_PRESSED);
        _invBtn.setMouseUpImage(SlickResources.BUTTON_INV_NORMAL);
        _invBtn.setMouseOverImage(SlickResources.BUTTON_INV_NORMAL);
        _invBtn.setId(BUTTON_INV);
        _invBtn.addButtonListener(this);
        addUIElement(_invBtn);


        _targetFrame = new TargetFrame();
        
        _statusFrame = new PlayerCharStatusFrame(self);
        
        /*
        _schedulerFrame = new SchedulerTestFrame(_game.getTaskScheduler());
        _schedulerFrame.setLocation(300, 300);
        addUIElement(_schedulerFrame);
        */

        _multiFrameDefault[MULTI_FRAME_UPPER] = _statusFrame;
        _multiFrameDefault[MULTI_FRAME_CENTRAL] = _targetFrame;
        _multiFrameDefault[MULTI_FRAME_LOWER] = _messageWindow;
        resetMultiFrame(MULTI_FRAME_UPPER);
        resetMultiFrame(MULTI_FRAME_CENTRAL);
        resetMultiFrame(MULTI_FRAME_LOWER);
        

        _miniStatus = new MiniStatusFrame(42, 37, self);
        addUIElement(_miniStatus);
        
        
        _messageWindow.addMessage("Welcome to Albareth.");
        

    }
    
    private void fillActionBar()
    {
        PlayerCharacter player = _frontend.getPlayerCharacter();
        int button = 0;
        for (Iterator it = player.getSpells().iterator(); it.hasNext(); )
        {
            _actionBar.setItem(button, (Spell)it.next());
            button++;
        }
        for (Iterator it = player.getAllItems().iterator(); it.hasNext(); )
        {
            Item item = (Item)it.next();
            if (item.isActivatable()) {
                _actionBar.setItem(button, item);
                button++;
            }
        }
    }

    public MessageWindow getMessageWindow()
    {
        return _messageWindow;
    }

    public DungeonWindow getDungeonWindow()
    {
        return _dungeonWindow;
    }
    
    public TargetFrame getTargetFrame()
    {
        return _targetFrame;
    }



    public void render(Graphics g) throws SlickException
    {
    }
    



    public void update(int delta) throws SlickException
    {
        _frontend.getGame().update(delta);
        //is our target dead? then cancel targeting it.
        Mobile targetMob = _frontend.getTargetMobile();
        if (targetMob != null && targetMob.isFlagSet(Mobile.FLAG_DEAD)) {
            _frontend.cancelTarget();
        }
        /*
        _dungeonWindow.update(delta);
        _targetFrame.update(delta);
        _currentMultiFrame.update(delta);
        */
        
        //super.update(container, game, delta);
    }



    
    public void keyPressedFallthrough(int key, char c)
    {
        if (_actionBar.keyPressed(key, c)) {
            return;
        }
        
        if (getMultiFrame(MULTI_FRAME_CENTRAL).getId() == 
            COMP_CONTEXTMENU_INVENTORY)
        {
            if (key == CMD_ABORT) {
                resetMultiFrame(MULTI_FRAME_CENTRAL);
                return;
            //} else {
            //    getMultiFrame(MULTI_FRAME_CENTRAL).keyPressed(key, c);
            }
            
        }
        
        Mobile self = _frontend.getPlayerCharacter();
        switch(key)
        {
            case CMD_MOVE_NORTH:
            case CMD_MOVE_NORTH_2:
                _frontend.playerWalk(Direction.NORTH);
                break;
            case CMD_MOVE_SOUTH:
            case CMD_MOVE_SOUTH_2:
            case CMD_MOVE_SOUTH_3:
                _frontend.playerWalk(Direction.SOUTH);
                break;
            case CMD_MOVE_EAST:
            case CMD_MOVE_EAST_2:
                _frontend.playerWalk(Direction.EAST);
                break;
            case CMD_MOVE_WEST:
            case CMD_MOVE_WEST_2:
                _frontend.playerWalk(Direction.WEST);
                break;
            case CMD_MOVE_NORTHWEST:
                _frontend.playerWalk(Direction.NORTH|Direction.WEST);
                break;
            case CMD_MOVE_SOUTHWEST:
                _frontend.playerWalk(Direction.SOUTH|Direction.WEST);
                break;
                /*
            case CMD_MOVE_NORTHEAST:
                _frontend.playerWalk(Direction.EAST|Direction.NORTH);
                break;
                */
                /*
            case CMD_MOVE_SOUTHEAST:
                _frontend.playerWalk(Direction.EAST|Direction.SOUTH);
                break;
                 */
                
            case CMD_ABORT:
                Action action = self.getActiveAction();
                if (action != null) {
                    action.abort();
                    
                } else if (_frontend.getTargetMobile() != null) {
                    _frontend.cancelTarget();
                    _targetFrame.setTarget(null);
                    
                } else {
                    resetMultiFrame(MULTI_FRAME_UPPER);
                    resetMultiFrame(MULTI_FRAME_CENTRAL);
                    resetMultiFrame(MULTI_FRAME_LOWER);
                    _mapBtn.setPressed(false);
                    _eqBtn.setPressed(false);
                    _invBtn.setPressed(false);
                }
                break;
                
            case CMD_MAP:
                //show map overview or close it again:
                toggleMap();
                break;
            case CMD_INVENTORY:
            case CMD_INVENTORY_2:
                //toggle inventory:
                toggleInventory();
                break;
                
            case CMD_CHAR_INFO:
            case CMD_CHAR_INFO_2:
                //toggle between equipment and status:
                toggleStatus();
                break;

            case CMD_USE:
                //use first item lying on ground:
                Mobile player = _frontend.getPlayerCharacter();
                Location loc = player.getLocation();
                List items = loc.getItems();
                if (items != null) {
                    Item first = (Item)items.get(0);
                    if (first.isActivatable()) {
                        ((Activatable)first).activate(player);
                    }
                }
                break;

            default:
                if (FToolkit.rand(5) == 0) {
                    _frontend.infoMsg("Press H for Help");
                }
                break;
                    
        }
    }
    
    /**
     * Toggle between inventory and message frame.
     */
    public void toggleInventory()
    {
        _frontend.playSound(Resources.SOUND_BUTTON_POP);
        if (getMultiFrame(MULTI_FRAME_LOWER).getId() == COMP_INVENTORY)
        {
            _invBtn.setPressed(false);
            resetMultiFrame(MULTI_FRAME_LOWER);
            _inventoryFrame = null;
        } else {
            showInventory();
        }
    }

    /**
     * Toggle between equip frame and status frame.
     */
    public void toggleStatus()
    {
        _frontend.playSound(Resources.SOUND_BUTTON_POP);
        if (getMultiFrame(MULTI_FRAME_UPPER).getId() == COMP_EQUIPMENT) 
        {
            resetMultiFrame(MULTI_FRAME_UPPER);
            _eqBtn.setPressed(false);
            _mapBtn.setPressed(false);

        } else {
            //show equipment:
            EquipmentFrame equipment = new EquipmentFrame(_frontend);
            setMultiFrame(MULTI_FRAME_UPPER, equipment);
            _eqBtn.setPressed(true);
            _mapBtn.setPressed(false);

        }

    }
    
    public void toggleMap()
    {
        _frontend.playSound(Resources.SOUND_BUTTON_POP);
        if (getMultiFrame(MULTI_FRAME_UPPER).getId() == COMP_OVERVIEW) 
        {
            resetMultiFrame(MULTI_FRAME_UPPER);
            _eqBtn.setPressed(false);
            _mapBtn.setPressed(false);
        } else {
            MapOverviewFrame overview =
                new MapOverviewFrame(
                        3, _frontend.getPlayerCharacter()
                );
            setMultiFrame(MULTI_FRAME_UPPER, overview);
            _eqBtn.setPressed(false);
            _mapBtn.setPressed(true);
        }

    }
    
    public void showInventory()
    {
        _inventoryFrame =
            new InventoryFrame(_frontend.getPlayerCharacter());
        //_inventoryFrame.addMenuListener(this);
        setMultiFrame(MULTI_FRAME_LOWER, _inventoryFrame);
        _invBtn.setPressed(true);

    }

    //methods to manage the three multi frames:
    
    public void setMultiFrame(int multiFrame, UIElement elem)
    {
        if (_multiFrame[multiFrame] == elem) {
            return;
        }
        removeUIElement(_multiFrame[multiFrame]);
        elem.setX(MULTI_FRAME_X[multiFrame]);
        elem.setY(MULTI_FRAME_Y[multiFrame]);
        elem.setWidth(MULTI_FRAME_WIDTH[multiFrame]);
        elem.setHeight(MULTI_FRAME_HEIGHT[multiFrame]);
        addUIElement(elem);
        elem.doLayout();
        _multiFrame[multiFrame] = elem;
    }
    
    private void resetMultiFrame(int multiFrame)
    {
        setMultiFrame(multiFrame, _multiFrameDefault[multiFrame]);
    }

    private UIElement getMultiFrame(int multiFrame)
    {
        return _multiFrame[multiFrame];
    }
    

    public void keyReleasedFallthrough(int key, char c)
    {
        _actionBar.processKeyReleased(key, c);
    }

    public void mouseMovedFallthrough(int newx, int newy)
    {
    }

    public void mouseDownFallthrough(int button, int x, int y)
    {
    }

    public void mouseUpFallthrough(int button, int x, int y)
    {
    }

    public void menuCloseRequested(BasicMenuFrame menu)
    {
    }

    /*
    public void menuItemSelected(BasicMenuFrame menu, MenuItem selectedItem)
    {
        if (menu.getId() == COMP_INVENTORY)
        {
            Item item = (Item)selectedItem.getReferencedObject();
            inventoryItemSelected(item);

        } else if (menu.getId() == COMP_CONTEXTMENU_INVENTORY)
        {
            Item item = ((InventoryContextMenu)menu).getHandledItem();
            inventoryContextMenuItemSelected(item, selectedItem.getId());
            
        }
    }
    */
    
    /*
    private void inventoryItemSelected(Item item)
    {
        BasicMenuFrame contextMenu = new InventoryContextMenu(item);
        contextMenu.addMenuListener(this);
        setMultiFrame(MULTI_FRAME_CENTRAL, contextMenu);
    }
    */


    /*
    private void inventoryContextMenuItemSelected(Item item, int action)
    {
        Mobile player = _frontend.getPlayerCharacter();
        
        if (action == InventoryContextMenu.ACTION_DROP)
        {
            player.dropItem(item);
            UIElement bottomElem = getMultiFrame(MULTI_FRAME_LOWER);
            if (bottomElem.getId() == COMP_INVENTORY) {
                ((InventoryFrame)bottomElem).removeItemByObj(item);
            }
        } else if (action == InventoryContextMenu.ACTION_USE)
        {
            //TODO
            
        } else if (action == InventoryContextMenu.ACTION_EQUIP)
        {
            //TODO

        } else if (action == InventoryContextMenu.ACTION_QUICKBAR)
        {
            //TODO
        }
        
        resetMultiFrame(MULTI_FRAME_CENTRAL);
    }
    */


    public void addMessage(String text, Color color)
    {
        if (_messageWindow != null) {
            _messageWindow.addMessage(text, color);
        }
    }
    
    public void addOverheadMessage(Entity emitter, String message,
            Color color, Easing easing)
    {
        if (_dungeonWindow != null) {
            _dungeonWindow.addOverheadMessage(emitter, message, color, easing);
        }
    }

    
    public void addParticleEffect(GameEffect effect)
    {
        if (_dungeonWindow != null) {
        _dungeonWindow.addEffect(effect);
        }
    }

    
    public void displayContainer(ItemContainer itemContainer)
    {
        //message to player about opening?
        if (_containerFrame == null ||
                _containerFrame.getShownContainer() != itemContainer)
        {
            _frontend.getPlayerCharacter().eventMsg(
                    "You open the " + itemContainer.getName() + "."
            );
        }

        _containerFrame = new ContainerFrame(_frontend, itemContainer);
        setMultiFrame(MULTI_FRAME_CENTRAL, _containerFrame);
        
        if (getMultiFrame(MULTI_FRAME_LOWER).getId() != COMP_INVENTORY)
        {
            showInventory();
        }
    }




    /**
     * Called by the frontend when a container's content changed.
     */
    public void itemContainerChanged(ItemContainer cont)
    {
        if (cont == _frontend.getPlayerCharacter().getBackpack())
        {
            if (_inventoryFrame != null) {
                _inventoryFrame.itemContainerChanged(cont);
            }
            if (_actionBar != null) {
                _actionBar.itemContainerChanged(cont);
            }
        } else if (_containerFrame != null &&
                _containerFrame.getShownContainer() == cont)
        {
            _containerFrame.itemContainerChanged(cont);
        }

        
    }

    protected void leave() throws SlickException
    {
        // TODO Auto-generated method stub
        
    }

    public void buttonPressed(UIButton button)
    {
        if (button.getId() == BUTTON_EQ) {
            toggleStatus();
        } else if  (button.getId() == BUTTON_INV) {
            toggleInventory();
        } else if  (button.getId() == BUTTON_MAP) {
            toggleMap();
        }        

    }

    public void showDialog(ScrollDialog dialog)
    {
        addUIElement(dialog);
    }









}
